(ns scripts.cljmon.chart
  (:use chart.jfree
        scripts.cljmon.parser
        util.file
        util.utils)
  (:require clojure.string))



(defn- column-index [columns c]
  (second (first (filter #(= (first %) c) columns))))

(defn- to-number [text]
  (if (= text "-")
    0
    (if (= (.indexOf text (int \.)) -1)
      (Long/parseLong text)
      (Double/parseDouble text))))

(def default-x-column "Timestamp")

(defn- select-column
  ([x y {:keys [headers records named-records columns]}]
    (for [r named-records :let [k (get r x) v (get r y)] :when (and v (not= v "-"))]
      [(java.util.Date. (Long/parseLong k)) (to-number v)]))
  ([y everything]
    (select-column default-x-column y everything)))

(def ^:private ^:dynamic *cljmon-process-name*)
(defn- chart-title [column]
  (if-let [[_ pid u host] (re-matches #"(.+) ((?:VM|Memory) \(\w+\)|CPU)(?: @ (\S+))?" column)]
    (let [pid (if (re-matches #"\d+" pid) (Integer/parseInt pid) pid)
          k (if host [host pid] pid)
          name (get *cljmon-process-name* k)]
      (str pid (when (and (integer? pid) name) (str "/" name)) " " u (when host (str " @ " host))))
    column))

(defn- chart-y-range [chart column]
  (if (re-matches #".+ CPU" column)
    (set-y-range chart 0 100)
    chart))

(defn column-chart [column everything]
  (let [data (select-column column everything)
        data (if (vector? *range*) (filter #(<= (first *range*) (.getTime (first %)) (second *range*)) data) data)]
    (-> (chart :time (chart-title column) "Time" column (dataset :time data))
      (use-color :blue)
      (set-x-date-format  "MM-dd HH:mm")
      set-y-from-zero
      (chart-y-range column))))

(defn- chart-file-name [column]
  (str (clojure.string/replace column #"[:/\\]" "_") ".png")) ; TODO

(defn generate-png [column everything dir]
  (let [path (file-join dir (chart-file-name column))]
    (save (column-chart column everything) path)
    path))

(def ignored #{"Timestamp" "Date" "Time"})

(defn- useful-columns [columns]
  (remove ignored columns))

(defn generate-charts
  ([logfile dir]
    (let [{:keys [headers records named-records columns comments] :as everything} (if (sequential? logfile) (parse-multi-logs logfile) (parse-log logfile))
          headers (useful-columns headers)]
      (doall (map #(vector % (generate-png % everything dir)) headers))))
  ([logfile]
    (generate-charts logfile (file-dirname logfile))))

(defn- column-link [column]
  (-> column
    (clojure.string/replace-first #"\s*\(\w+\)" "")
    (clojure.string/replace #"\s" "-")))

(defn- robust-compare [x y]
  (cond
    (and (number? x) (string? y)) -1
    (and (string? x) (number? y)) 1
    (and (sequential? x) (sequential? y)) (loop [x x
                                                 y y]
                                            (cond
                                              (and (empty? x) (empty? y)) 0
                                              (empty? x) -1
                                              (empty? y) 1
                                              :else (let [v (robust-compare (first x) (first y))]
                                                      (if (zero? v)
                                                        (recur (next x) (next y))
                                                        v))))
    :else (compare x y)))

(defn generate-html
  ([logfile dir]
    (let [path (file-join dir "cljmon.html")
          comments (if (sequential? logfile) (apply merge-with merge (map parse-comments logfile)) (parse-comments logfile))
          process-dict (into {}
                         (for [[k v] (:process comments)]
                           [k (second (re-find #"\bname:\s*([^,]+)" v))]))]
      (when-not (file-exists? dir)
        (mkdir dir))
      (binding [*cljmon-process-name* process-dict]
        (let [charts (generate-charts logfile dir)]
          (file-save
            (with-out-str
              (println "<a name='__top'></a>")
              (doseq [[p pinfo] (sort robust-compare (for [[k v] (:process comments)] [(if (sequential? k) k [nil k]) v]))]
                (let [[host pid] p
                      name (if host (str pid "@" host) pid)
                      links (for [t ["CPU" "VM" "Memory"]]
                              (if host
                                (format "#%s-%s-@-%s" pid t host)
                                (format "#%s-%s" pid t)))
                      [cpu vm mem] links]
                  (println (format "<br>%s: <a href='%s'>CPU</a>, <a href='%s'>Virtual Memory</a>, <a href='%s'>Physical Memory</a> <br>%s<br>" name cpu vm mem pinfo))))
              (println "<br>")
              (doseq [[header file] charts]
                (println (format "<br><h2><a name='%s' href='#__top'>%s</a></h2><br><img src='%s'><br>" (column-link header) header (file-basename file)))))
            path)))
      path))
  ([logfile]
    (generate-html logfile (file-dirname logfile))))


